﻿Public Class Comment
    Public Shared Sub SingleLineComment(ByVal editor As ScintillaNET.Scintilla)
        Dim startLine As ScintillaNET.Line = editor.Selection.Range.StartingLine
        Dim curPosition As Integer = editor.CurrentPos
        Dim isAfterComment As Boolean = (startLine.SelectionStartPosition + startLine.Indentation < curPosition)

        '' Make current line as comment
        If startLine.Indentation + 2 < startLine.Length AndAlso _
            startLine.Text.Substring(startLine.Indentation, 2) = "//" Then
            startLine.Text = startLine.Text.Substring(0, startLine.Indentation) & "//" & startLine.Text.Substring(startLine.Indentation).TrimEnd()
            curPosition += If(isAfterComment, 0, 2)
        Else
            startLine.Text = startLine.Text.Substring(0, startLine.Indentation) & "// " & startLine.Text.Substring(startLine.Indentation).TrimEnd()
            curPosition += If(isAfterComment, 0, 3)
        End If

        '' Restore caret position
        editor.CurrentPos = curPosition
    End Sub

    Public Shared Sub MultipleLineComment(ByVal editor As ScintillaNET.Scintilla)
        '' Find the minimun indentation among selected line(s)
        Dim minIndendation As Integer = Integer.MaxValue
        Dim startLine As ScintillaNET.Line = editor.Selection.Range.StartingLine
        Dim endLine As ScintillaNET.Line = editor.Selection.Range.EndingLine

        For i As Integer = startLine.Number To endLine.Number
            Dim line As ScintillaNET.Line = editor.Lines(i)
            If line.Indentation < minIndendation Then
                minIndendation = line.Indentation
            End If
        Next

        '' Construct commented code from selection
        Dim newText As String = ""
        Dim newStart As Integer = editor.Selection.Start
        Dim newLength As Integer = editor.Selection.Length

        If startLine.StartPosition + startLine.Indentation < _
           newStart Then
            If startLine.Indentation + 2 < startLine.Length AndAlso _
               startLine.Text.Substring(startLine.Indentation, 2) = "//" Then
                newStart += 2
                newLength -= 2
            Else
                newStart += 3
                newLength -= 3
            End If
        End If

        For i As Integer = startLine.Number To endLine.Number
            Dim line As ScintillaNET.Line = editor.Lines(i)

            If line.Indentation + 2 < line.Length AndAlso _
                line.Text.Substring(line.Indentation, 2) = "//" Then
                newText &= line.Text.Substring(0, minIndendation) & "//" & line.Text.Substring(minIndendation)
                newLength += 2
            Else
                newText &= line.Text.Substring(0, minIndendation) & "// " & line.Text.Substring(minIndendation)
                newLength += 3
            End If
        Next

        '' Replace old code with newly commented code
        editor.Selection.Start = startLine.StartPosition()
        editor.Selection.End = endLine.EndPosition()
        editor.Selection.Text = newText.TrimEnd()

        '' Update new selection
        editor.Selection.Start = newStart
        editor.Selection.Length = newLength
    End Sub

    Private Shared Function AdjustCaretUncommentLine(ByVal line As ScintillaNET.Line, ByVal position As Integer, ByVal type As String) As Integer
        If position >= line.StartPosition And position <= line.EndPosition Then
            If position < line.StartPosition + line.Indentation Then
                '' No effect
            ElseIf position < line.StartPosition + line.Indentation + type.Length Then
                position = line.StartPosition + line.Indentation
            Else
                position -= type.Length
            End If

            Return position
        Else
            '' Caret position is in provided line
            Return position
        End If
    End Function

    Private Shared Function GetLineCommentType(ByVal line As ScintillaNET.Line) As String
        If line.Indentation + 3 < line.Length _
           AndAlso line.Text.Substring(line.Indentation, 3) = "// " Then
            Return "// "
        ElseIf line.Indentation + 2 < line.Length _
            AndAlso line.Text.Substring(line.Indentation, 2) = "//" Then
            Return "//"
        Else
            Return ""
        End If
    End Function

    Public Shared Sub SingleLineUncomment(ByVal editor As ScintillaNET.Scintilla)
        Dim startLine As ScintillaNET.Line = editor.Selection.Range.StartingLine
        Dim curPosition As Integer = editor.CurrentPos

        Dim commentType As String = GetLineCommentType(startLine)
        Select Case commentType
            Case "//", "// "
                If curPosition < startLine.StartPosition + startLine.Indentation Then
                    ''  Caret is before the single line comment
                    '' No effect
                ElseIf curPosition < startLine.StartPosition + startLine.Indentation + commentType.Length Then
                    '' Caret is inside the comment characters
                    curPosition = startLine.StartPosition + startLine.Indentation
                Else
                    '' Caret is after the comment characters
                    curPosition -= commentType.Length
                End If

                '' Remove comment
                startLine.Text = startLine.Text.Substring(0, startLine.Indentation) & startLine.Text.Substring(startLine.Indentation + commentType.Length).TrimEnd()
        End Select

        '' Restore caret position
        editor.CurrentPos = curPosition
    End Sub

    Public Shared Sub MultipleLineUncomment(ByVal editor As ScintillaNET.Scintilla)
        Dim startLine As ScintillaNET.Line = editor.Selection.Range.StartingLine
        Dim endLine As ScintillaNET.Line = editor.Selection.Range.EndingLine

        '' Construct uncommented code from selection
        Dim newText As String = ""
        Dim newStart As Integer = editor.Selection.Start
        Dim newLength As Integer = editor.Selection.Length

        '' Adjust the selection position
        Dim commentType As String = GetLineCommentType(startLine)
        Select Case commentType
            Case "//", "// "
                If newStart < startLine.StartPosition + startLine.Indentation Then
                    ''  Caret is before the single line comment
                    '' No effect
                ElseIf newStart < startLine.StartPosition + startLine.Indentation + commentType.Length Then
                    '' Caret is inside the comment characters
                    Dim adjustment As Integer = newStart - startLine.StartPosition + startLine.Indentation
                    newStart -= adjustment
                    newLength += adjustment
                Else
                    '' Caret is after the comment characters
                    newStart -= commentType.Length
                    newLength += commentType.Length
                End If
        End Select

        '' Remove comment from each line
        For i As Integer = startLine.Number To endLine.Number
            Dim line As ScintillaNET.Line = editor.Lines(i)
            commentType = GetLineCommentType(line)

            Select Case commentType
                Case "//", "// "
                    newLength -= commentType.Length
                    newText &= line.Text.Substring(0, line.Indentation) & line.Text.Substring(line.Indentation + commentType.Length)
                Case ""
                    newText &= line.Text
            End Select
        Next

        '' Replace old code with newly commented code
        editor.Selection.Start = startLine.StartPosition()
        editor.Selection.End = endLine.EndPosition()
        editor.Selection.Text = newText.TrimEnd()

        '' Update new selection
        editor.Selection.Start = newStart
        editor.Selection.Length = newLength
    End Sub
End Class
